// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef REMOTING_PROTOCOL_ICE_TRANSPORT_CHANNEL_H_
#define REMOTING_PROTOCOL_ICE_TRANSPORT_CHANNEL_H_

#include <string>

#include "base/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/threading/thread_checker.h"
#include "base/timer/timer.h"
#include "remoting/protocol/network_settings.h"
#include "remoting/protocol/transport.h"
// TODO(zhihuang):Replace #include by forward declaration once proper
// inheritance is defined for cricket::IceTransportInternal and
// cricket::P2PTransportChannel.
#include "third_party/webrtc/p2p/base/ice_transport_internal.h"
// TODO(johan): Replace #include by forward declaration once proper inheritance
// is defined for rtc::PacketTransportInterface and cricket::TransportChannel.
#include "third_party/webrtc/p2p/base/packet_transport_internal.h"
#include "third_party/webrtc/rtc_base/third_party/sigslot/sigslot.h"

namespace cricket {
class Candidate;
class P2PTransportChannel;
class PortAllocator;
}  // namespace cricket

namespace remoting {
namespace protocol {

class P2PDatagramSocket;
class TransportContext;

class IceTransportChannel : public sigslot::has_slots<> {
 public:
  class Delegate {
   public:
    Delegate() {}
    virtual ~Delegate() {}

    // Called to pass ICE credentials to the session. Used only for STANDARD
    // version of ICE, see SetIceVersion().
    virtual void OnChannelIceCredentials(IceTransportChannel* transport,
                                           const std::string& ufrag,
                                           const std::string& password) = 0;

    // Called when the transport generates a new candidate that needs
    // to be passed to the AddRemoteCandidate() method on the remote
    // end of the connection.
    virtual void OnChannelCandidate(IceTransportChannel* transport,
                                      const cricket::Candidate& candidate) = 0;

    // Called when transport route changes. Can be called even before
    // the transport is connected.
    virtual void OnChannelRouteChange(IceTransportChannel* transport,
                                        const TransportRoute& route) = 0;

    // Called when when the channel has failed to connect or reconnect.
    virtual void OnChannelFailed(IceTransportChannel* transport) = 0;

    // Called when the channel is about to be deleted.
    virtual void OnChannelDeleted(IceTransportChannel* transport) = 0;
  };

  typedef base::OnceCallback<void(std::unique_ptr<P2PDatagramSocket>)>
      ConnectedCallback;

  explicit IceTransportChannel(
      scoped_refptr<TransportContext> transport_context);

  IceTransportChannel(const IceTransportChannel&) = delete;
  IceTransportChannel& operator=(const IceTransportChannel&) = delete;

  ~IceTransportChannel() override;

  // Connects the channel and calls the |callback| after that.
  void Connect(const std::string& name,
               Delegate* delegate,
               ConnectedCallback callback);

  // Sets remote ICE credentials.
  void SetRemoteCredentials(const std::string& ufrag,
                            const std::string& password);

  // Adds |candidate| received from the peer.
  void AddRemoteCandidate(const cricket::Candidate& candidate);

  // Name of the channel. Used to identify the channel and disambiguate
  // candidates it generates from candidates generated by parallel connections.
  const std::string& name() const;

  // Returns true if the channel is already connected.
  bool is_connected() const;

 private:
  void OnPortAllocatorCreated(
      std::unique_ptr<cricket::PortAllocator> port_allocator);

  void NotifyConnected();

  // Signal handlers for cricket::IceTransportInternal.
  void OnCandidateGathered(cricket::IceTransportInternal* ice_transport,
                           const cricket::Candidate& candidate);
  void OnRouteChange(cricket::IceTransportInternal* ice_transport,
                     const cricket::Candidate& candidate);
  void OnWritableState(rtc::PacketTransportInternal* transport);

  // Callback for TransportChannelSocketAdapter to notify when the socket is
  // destroyed.
  void OnChannelDestroyed();

  void NotifyRouteChanged();

  // Tries to connect by restarting ICE. Called by |reconnect_timer_|.
  void TryReconnect();

  scoped_refptr<TransportContext> transport_context_;

  std::string name_;
  raw_ptr<Delegate> delegate_ = nullptr;
  ConnectedCallback callback_;
  std::string ice_username_fragment_;

  std::unique_ptr<cricket::PortAllocator> port_allocator_;

  std::string remote_ice_username_fragment_;
  std::string remote_ice_password_;
  std::list<cricket::Candidate> pending_candidates_;
  std::unique_ptr<cricket::P2PTransportChannel> channel_;
  int connect_attempts_left_;
  base::RepeatingTimer reconnect_timer_;

  base::ThreadChecker thread_checker_;

  base::WeakPtrFactory<IceTransportChannel> weak_factory_{this};
};

}  // namespace protocol
}  // namespace remoting

#endif  // REMOTING_PROTOCOL_ICE_TRANSPORT_CHANNEL_H_
